﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using System.Text.Json.Nodes;
using System.IO.IsolatedStorage;
using System.Diagnostics;
using System.Reflection.Metadata.Ecma335;
using System.IO;
using System.Text.Encodings.Web;
using System.Security.Cryptography;
using System.Drawing;
using System.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Runtime.CompilerServices;
using System.Reflection;

namespace Hong.Pan
{
    public class Baidu : IPan
    {
        /// <summary>
        /// baidu盘开发的client_id
        /// </summary>
        private string? client_id;

        /// <summary>
        /// 百度盘的存取key
        /// </summary>
        private string? access_token = null;

        /// <summary>
        /// 上传路径
        /// </summary>
        private string uploadPath = "/apps/我的盘/";

        /// <summary>
        /// 基础地址
        /// </summary>
        private static string baseUrl = "https://pan.baidu.com";


        public Baidu(IConfiguration config)
        {
            client_id = config["baidu_client_id"];
        }

        /// <summary>
        /// 网盘的登录地址
        /// </summary>
        public string LoginUrl
        {
            get
            {
                return "https://openapi.baidu.com/oauth/2.0/authorize?response_type=token&client_id=" + client_id + "&redirect_uri=oob&scope=basic,netdisk&login_type=sms";
            }
        }

        /// <summary>
        /// 判断是否登录
        /// </summary>
        /// <returns></returns>
        public bool IsLogin()
        {
            IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication();
            do
            {
                if (!file.FileExists("user.json"))
                {
                    break;
                }
                StreamReader reader = new StreamReader(file.OpenFile("user.json", System.IO.FileMode.Open));
                try
                {
                    string s = reader.ReadToEnd();
                    JsonNode? json = JsonObject.Parse(s);
                    if (json == null) break;
                    access_token = json["access_token"]?.GetValue<string>();
                    if (access_token == null) break;
                }
                catch
                {
                    break;
                }
                finally
                {
                    reader.Close();
                    file.Close();
                }
                return true;
            } while (false);

            return false;
        }

        /// <summary>
        /// 登录完成
        /// </summary>
        public bool Logined(string param)
        {
            if (param.IndexOf("login_success") != -1)
            {
                param = param.Substring(param.IndexOf("#") + 1);
                string[] para = param.Split('&');
                JsonObject json = new JsonObject();
                foreach (string s in para)
                {
                    string[] values = s.Split("=");
                    Debug.WriteLine($"para {values[0]}={values[1]}");
                    json[values[0]] = values[1];
                    if (values[0] == "access_token")
                    {
                        access_token = values[1];
                    }
                }
                IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication();
                IsolatedStorageFileStream fl = new IsolatedStorageFileStream("user.json", FileMode.Create, isoFile);
                fl.Write(Encoding.UTF8.GetBytes(json.ToString()));
                fl.Close();
                isoFile.Dispose();
                isoFile.Close();
                return true;
            }

            return false;
        }

        /// <summary>
        /// 盘的基本信息
        /// </summary>
        /// <returns></returns>
        public Task<JsonNode?> Info()
        {
            return Task.Run<JsonNode?>(async () =>
            {
                string url = $"{baseUrl}/rest/2.0/xpan/nas?method=uinfo&access_token={access_token}";
                JsonNode? jnl = await Https.Json(url);
                return jnl;
            });
        }

        /// <summary>
        /// 盘的基本信息
        /// </summary>
        /// <returns></returns>
        public Task<JsonNode?> quota()
        {
            return Task.Run<JsonNode?>(async () =>
            {
                string url = $"{baseUrl}/api/quota?checkfree=1&checkexpire=1&access_token={access_token}";
                JsonNode? jnl = await Https.Json(url);
                return jnl;
            });
        }

        /// <summary>
        /// 读文件或目录
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public Task<List<PanFileInfo>?> Read(string path)
        {
            PanFileInfo pf = new PanFileInfo { path = path, isdir = true };
            return Read(pf);
        }

        /// <summary>
        /// 读文件或目录
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public Task<List<PanFileInfo>?> Read(PanFileInfo file)
        {
            if (file.isdir)
            {
                return Task.Run<List<PanFileInfo>?>(async () =>
                {
                    List<PanFileInfo> list = new();

                    string path = UrlEncoder.Default.Encode(file.path);
                    string url = $"{baseUrl}/rest/2.0/xpan/file?method=list&dir={path}&access_token={access_token}";
                    JsonNode? jnl = await Https.Json(url);

                    if (jnl != null && ((int?)jnl["errno"]) == 0)
                    {

                        JsonArray? jsonArray = jnl["list"]?.AsArray();
                        if (jsonArray == null) return list;

                        foreach (JsonNode? jn in jsonArray)
                        {
                            if (jn != null)
                            {
                                PanFileInfo panFile = new PanFileInfo();

                                string? tmp = jn["path"]?.ToString();
                                if (tmp != null) panFile.path = tmp;
                                tmp = jn["server_filename"]?.ToString();
                                if (tmp != null) panFile.filename = tmp;
                                tmp = jn["fs_id"]?.ToString();
                                if (tmp != null) panFile.fid = tmp;

                                panFile.size = (long)jn["size"];
                                panFile.isdir = (int?)jn["isdir"] == 1 ? true : false;
                                if (panFile.isdir)
                                {
                                    panFile.icon = "/Resources/box.png";
                                }
                                else
                                {
                                    panFile.icon = "/Resources/file.png";
                                }
                                list.Add(panFile);
                                //Debug.WriteLine(jn);
                            }
                        }
                        return list;
                    }
                    else
                    {
                        return null;
                    }
                });

            }
            else
            {
                //return null;
                return Task.Run<List<PanFileInfo>?>(async () =>
                {
                    //List<PanFileInfo> list = new List<PanFileInfo>();
                    return null;
                });
            }
        }

        /// <summary>
        /// 读文件并下载保存到本地目录
        /// </summary>
        /// <param name="path">服务器路径</param>
        /// <param name="localpath">本地路径</param>
        /// <returns></returns>
        public Task<int> Read(string path, string localpath)
        {
            return Task.Run<int>(() =>
            {
                return 0;
            });
        }

        /// <summary>
        /// 读文件并下载保存到本地目录
        /// </summary>
        /// <param name="file">读文件</param>
        /// <param name="localpath">本地路径</param>
        /// <returns></returns>
        public async Task<int> Read(PanFileInfo file, string localpath, Action<long>? callback = null)
        {
            string url = $"{baseUrl}/rest/2.0/xpan/multimedia?method=filemetas&fsids=[{file.fid}]&dlink=1&access_token={access_token}";
            JsonNode? jnl = await Https.Json(url);
            //Debug.WriteLine(jnl);
            JsonArray? jsonArray = jnl["list"]?.AsArray();
            if (jsonArray == null) return 0;
            string? durl = jsonArray?[0]?["dlink"]?.ToString();
            if (durl == null) return 0;

            durl += $"&access_token={access_token}";
            localpath += "/" + file.filename;
            return await Https.DownloadFile(durl, localpath, callback);
        }

        /// <summary>
        /// 读文件并下载保存到本地目录
        /// </summary>
        /// <param name="file">读文件</param>
        /// <param name="localpath">本地路径</param>
        /// <returns></returns>
        public async Task<VideoInfo?> ReadVideo(PanFileInfo file)
        {
            string url = $"{baseUrl}/rest/2.0/xpan/file?method=streaming&access_token={access_token}&path={UrlEncoder.Default.Encode(file.path)}&type=M3U8_AUTO_720";
            HttpResponseMessage? response = await Https.GetStringAsync(url, "xpanvideo;netdisk;iPhone13;ios-iphone;15.1;ts");
            if (response == null) return null;
            VideoInfo vi = new();
            if (response.Content.Headers.GetValues("content-type").Any(s => s == "application/json; charset=utf-8"))
            {
                var jn = JsonNode.Parse(response.Content.ReadAsStream());
                vi.adTime = jn?["adTime"]?.GetValue<int>() ?? 0;
                vi.ltime = jn?["ltime"]?.GetValue<int>() ?? 0;
                vi.adToken = jn?["adToken"]?.GetValue<string>() ?? "";
            }
            if (vi.adToken.Length > 0) {
                url += "&adToken=" + UrlEncoder.Default.Encode(vi.adToken);
                response = await Https.GetStringAsync(url, "xpanvideo;netdisk;iPhone13;ios-iphone;15.1;ts");
                if (response == null) return null;
                vi.m3u8 = await response.Content.ReadAsStringAsync();
            }
            return vi;
        }

        /// <summary>
        /// 写文件
        /// </summary>
        /// <param name="path"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public Task<int> Write(string path, Byte[] data)
        {
            return Task.Run<int>(() =>
            {
                return 0;
            });
        }

        /// <summary>
        /// 写文件
        /// </summary>
        /// <param name="path"></param>
        /// <param name="localpath"></param>
        /// <returns></returns>
        public async Task<int> Write(string path, string localpath)
        {
            Debug.WriteLine(path);
            Debug.WriteLine(localpath);

            FileInfo fileInfo = new FileInfo(localpath);
            long blockLen = fileInfo.Length / (4 * 1024 * 1024);
            if (blockLen * 4 * 1024 * 1024 != fileInfo.Length)
            {
                blockLen++;
            }
            string[] blockList = new string[blockLen];
            byte[] filebytes = new byte[fileInfo.Length];
            using (FileStream fileStream = File.OpenRead(localpath))
            {
                for (int i = 0; i < blockLen; i++)
                {
                    MD5 md5 = MD5.Create();
                    byte[]? md = null;
                    int beginPos = 4 * 1024 * 1024 * i;
                    if (fileInfo.Length < beginPos + 4 * 1024 * 1024)
                    {
                        fileStream.Read(filebytes, beginPos, (int)fileInfo.Length - beginPos);
                        md = md5.ComputeHash(filebytes, beginPos, (int)fileInfo.Length - beginPos);
                    }
                    else
                    {
                        fileStream.Read(filebytes, beginPos, 4 * 1024 * 1024);
                        md = md5.ComputeHash(filebytes, beginPos, 4 * 1024 * 1024);
                    }
                    string md5str = string.Empty;
                    for (int j = 0; j < md.Length; j++)
                    {
                        md5str += md[j].ToString("x2");
                    }
                    blockList[i] = md5str;
                }
            }
            //预上传文件
            string url = $"{baseUrl}/rest/2.0/xpan/file?method=precreate&access_token={access_token}";
            HttpContent content = new StringContent("path=" + HttpUtility.UrlEncode(path) + "&size=" + fileInfo.Length + "&isdir=0&autoinit=1&rtype=0&block_list=" + JsonSerializer.Serialize(blockList));
            JsonNode? jn = await Https.PostJson(url, content);
            if ((int?)jn?["errno"] != 0)
            {
                Debug.WriteLine("get uploadid error");
                return -1;
            }
            string? uploadid = jn?["uploadid"]?.ToString();
            if ((int?)jn?["return_type"] == 2 && blockList.Length == 0)
            {
                return await Create(path, fileInfo.Length, uploadid, JsonSerializer.Serialize(blockList));
            }
            //上传分片
            for (int partseq = 0; partseq < blockList.Length; partseq++)
            {
                url = $"https://d.pcs.baidu.com/rest/2.0/pcs/superfile2?method=upload&access_token={access_token}&type=tmpfile&path=" + HttpUtility.UrlEncode(path) + "&uploadid=" + uploadid + "&partseq=" + partseq;
                MultipartFormDataContent formcontent = new MultipartFormDataContent();
                if (fileInfo.Length < 4 * 1024 * 1024 * (partseq + 1))
                {
                    formcontent.Add(new ByteArrayContent(filebytes, 4 * 1024 * 1024 * partseq, (int)fileInfo.Length - 4 * 1024 * 1024 * partseq), "file", Path.GetFileName(localpath));
                }
                else
                {
                    formcontent.Add(new ByteArrayContent(filebytes, 4 * 1024 * 1024 * partseq, 4 * 1024 * 1024), "file", Path.GetFileName(localpath));
                }
                jn = await Https.PostJson(url, formcontent);
                if (jn?["md5"] == null)
                {
                    Debug.WriteLine("upload file error");
                    return -3;
                }
            }
            //创建文件
            return await Create(path, fileInfo.Length, uploadid, JsonSerializer.Serialize(blockList));
        }

        /// <summary>
        /// 创建文件
        /// </summary>
        private async Task<int> Create(string path, long size, string? uploadid, string block_list)
        {
            string url = $"{baseUrl}/rest/2.0/xpan/file?method=create&access_token={access_token}";
            string reqdata = "path=" + HttpUtility.UrlEncode(path) + "&size=" + size + "&isdir=0&rtype=0";
            if (uploadid != null)
            {
                reqdata += "&uploadid=" + uploadid + "&block_list=" + block_list;
            }
            Debug.WriteLine(reqdata);
            JsonNode? jn = await Https.PostJson(url, new StringContent(reqdata));
            if ((int?)jn?["errno"] != 0)
            {
                Debug.WriteLine("create file error");
                return -2;
            }
            return 0;
        }

        /// <summary>
        /// 删除文件
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public async Task<int> Delete(string path)
        {
            string url = $"{baseUrl}/rest/2.0/xpan/file?method=filemanager&opera=delete&access_token={access_token}";
            string reqdata = "async=1&filelist=[\"" + path + "\"]";
            Debug.WriteLine(reqdata);
            JsonNode? jn = await Https.PostJsonUtf8(url, new StringContent(reqdata));
            if ((int?)jn?["errno"] != 0)
            {
                Debug.WriteLine("Delete file error");
                return -2;
            }
            return 0;
        }
    }
}
